﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Chefbook.DataObjects;
using Chefbook.HelperMethods;
using System.Globalization;
using System.ComponentModel;

namespace Chefbook.DataObjects
{
    /// <summary>
    /// Represents an Ingredient in a Recipe
    /// Ingredients are represented as [Amount][Units][Name]
    /// Examples: [1][ ][Apple], [3.5][Tablespoon][Brown sugar]
    /// </summary>
    public class Ingredient : INotifyPropertyChanged
    {
        private float amount;
        private string name, units;

        /// <summary>
        /// Gets or sets the name of the ingredient
        /// </summary>
        public string Name 
        {
            get { return name; }
            set
            {
                if (name != value)
                {
                    name = value.ThrowIfNull("Name");
                    this.OnPropertyChanged("Name");
                }
            }
        }

        /// <summary>
        /// Gets or sets the amount of the ingredient, specified in the listed units.  Use zero for "To taste"
        /// </summary>
        public float Amount {
            get { return amount; }
            set
            {
                ParameterValidation.ValidateArgument(() => value >= 0, "Amount");
                if (amount != value)
                {
                    amount = value;
                    this.OnPropertyChanged("Amount");
                }
            }
        }

        /// <summary>
        /// Gets or sets the units this ingredient is measured in.  May be String.Empty if representing whole things, such as an apple
        /// </summary>
        public string Units
        {
            get { return units; }
            set
            {
                if (units != value)
                {
                    units = value.ThrowIfNull("Units");
                    this.OnPropertyChanged("Units");
                }
            }
        }

        private Recipe recipe;
        /// <summary>
        /// Gets the recipe that this ingredient is a part of
        /// </summary>
        public Recipe Recipe
        {
            get { return recipe; }
            set
            {
                if (recipe != value)
                {
                    if (recipe != null)
                        throw new InvalidOperationException("Cannot reassign recipe once set");
                    else
                    {
                        if (recipe != value)
                        {
                            recipe = value;
                            if (!recipe.Ingredients.Contains(this))
                            {
                                recipe.AddIngredient(this);
                            }
                            OnPropertyChanged("Recipe");
                        }
                    }
                }                
            }
        }

        /// <summary>
        /// Creates a new Ingredient
        /// </summary>
        public Ingredient() : this(null) { }

        /// <summary>
        /// Creates a new Ingredient in the specified recipe
        /// </summary>
        /// <param name="recipeContainingIngredient">The recipe this ingredient is added to</param>
        public  Ingredient(Recipe recipeContainingIngredient)
        {
            name = string.Empty;
            units = string.Empty;
            recipeContainingIngredient.PerformIfNotNull(() => recipeContainingIngredient.AddIngredient(this));
        }

        /// <summary>
        /// Checks whether this Ingredient has value equality to the parameter passed in
        /// </summary>
        /// <param name="value">The Ingredient to check this against</param>
        /// <returns>True of they are equal in value, otherwise false</returns>
        public bool ValueEquals(object value)
        {
            var ing = value as Ingredient;
            if (ing == null) return false;
            return (ing.amount == this.amount &&
                ing.name == this.name &&
                ing.units == this.units);
        }

        /// <summary>
        /// The ingredient in a nice display format.  Examples:
        /// 2 lb chicken breasts
        /// Pepper to taste
        /// 3 apples
        /// </summary>
        /// <returns>Returns a string representation of the Ingredient</returns>
        public override string ToString()
        {
            string retUnits = !string.IsNullOrEmpty(units) ? " " + units : string.Empty;
            string retName = !string.IsNullOrEmpty(name) ? " " + name: string.Empty;
            if (amount > 0)
                return string.Format(CultureInfo.CurrentUICulture, "{0}{1}{2}", amount, retUnits, retName );
            string cappedName = name[0].ToString().ToUpperInvariant() + name.Substring(1);
            return string.Format(CultureInfo.CurrentUICulture, "{0} to taste", cappedName);
        }

        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
